package cn.stylefeng.guns.modular.business.service.impl;

import cn.stylefeng.guns.base.pojo.page.LayuiPageFactory;
import cn.stylefeng.guns.base.pojo.page.LayuiPageInfo;
import cn.stylefeng.guns.core.RecoverConst;
import cn.stylefeng.guns.modular.business.entity.Certification;
import cn.stylefeng.guns.modular.business.mapper.CertificationMapper;
import cn.stylefeng.guns.modular.business.model.dto.CertificationDto;
import cn.stylefeng.guns.modular.business.model.params.CertificationParam;
import cn.stylefeng.guns.modular.business.model.params.MessageDTO;
import cn.stylefeng.guns.modular.business.model.params.MessageType;
import cn.stylefeng.guns.modular.business.model.params.WxMessageDTO;
import cn.stylefeng.guns.modular.business.model.result.CertificationResult;
import cn.stylefeng.guns.modular.business.service.CertificationService;
import cn.stylefeng.guns.modular.business.service.MessageNoticeService;
import cn.stylefeng.guns.modular.business.service.MessageService;
import cn.stylefeng.guns.sys.core.constant.CertificationStatus;
import cn.stylefeng.guns.sys.core.util.HttpUtil;
import cn.stylefeng.guns.sys.core.util.StringUtils;
import cn.stylefeng.guns.sys.modular.system.entity.User;
import cn.stylefeng.guns.sys.modular.system.service.UserService;
import cn.stylefeng.roses.core.util.ToolUtil;
import cn.stylefeng.roses.kernel.model.exception.ServiceException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sun.misc.BASE64Encoder;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.Serializable;
import java.security.Key;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.TimeZone;

/**
 * <p>
 * 实名认证 服务实现类
 * </p>
 *
 * @author pzp
 * @since 2019-07-25
 */
@Service("certificationService")
@Log4j2
public class CertificationServiceImpl extends ServiceImpl<CertificationMapper, Certification> implements CertificationService {

    @Autowired
    private UserService userService;

    @Autowired
    MessageService messageService;

    @Autowired
    private MessageNoticeService messageNoticeService;
    @Value("${calc.secretId}")
    private String secretId;
    @Value("${calc.secretKey}")
    private String secretKey;
    @Value("${calc.url}")
    private String calcUrl;
    @Override
    public void add(CertificationParam param) {
        Certification entity = getEntity(param);
        this.save(entity);
    }

    /**
     * 实名认证
     *
     * @param certificationDto
     * @author pzp
     * @Date 2019-07-25
     */
    @Override
    public void addOrUpdate(CertificationDto certificationDto) {
        //校验用户是否做过实名认证
        if (StringUtils.isEmpty(certificationDto.getName()) || StringUtils.isEmpty(certificationDto.getIdCard())) {
            throw new ServiceException(10001, "身份号或姓名不能为空");
        }
        Certification certification = this.getOne(new QueryWrapper<Certification>().eq("user_id", certificationDto.getUserId()));

        Integer status = CertificationStatus.APPROVING.getCode();
        //添加第三方实名认证 2019-09-28
        if (null == certification) {
            //没有实名认证的调用实名认证接口
            certification = new Certification();
            ToolUtil.copyProperties(certificationDto, certification);
            certification.setDeleteFlag(0);
            certification.setCreateTime(new Date());
            certification.setUpdateTime(new Date());
            //调用实名认证接口
            status = this.thirdCertification(certification);
            certification.setStatus(status);
            this.save(certification);
        } else {
            if (certification.getStatus() == 1) {
                throw new ServiceException(400, "用户已做实名认证");
            }
            ToolUtil.copyProperties(certificationDto, certification);
            certification.setUpdateTime(new Date());
            //调用实名认证接口
            status = this.thirdCertification(certification);
            certification.setStatus(status);
            this.updateById(certification);
        }
        User user2 = new User();
        user2.setUserId(certificationDto.getUserId());
        user2.setCertificationStatus(status);
        user2.setRealName(certificationDto.getName());
        userService.updateById(user2);
    }

    private Integer thirdCertification(Certification certification) {
        //调用第三方实名接口
        Integer status = CertificationStatus.APPROVING.getCode();
        try {
            JSONObject jsonObject = this.thirdCertification(certification.getName(), certification.getIdCard());
            String errorCode = jsonObject.getString("code");
            if (errorCode.equals("0")) {
                //接口调用成功
                JSONObject json = jsonObject.getJSONObject("result");
                String flag = json.getString("res");
                if (flag.equals("1")) {
                    status = CertificationStatus.PASS.getCode();
                    certification.setDescription("调用第三方实名认证成功");
                    //实名认证通过信息
                    User user = new User();
                    user.setUserId(certification.getUserId());
                    user = userService.getById(user);
                    if (null != user) {
                        this.sendMessageToUser(certification, user.getOpenid());
                    }
                } else {
                    //认证失败发送消息
                    status = CertificationStatus.REFUSE.getCode();
                    certification.setDescription(json.getString("description"));
                    messageNoticeService.add("实名认证失败通知", certification.getDescription(), "1", certification.getUserId());
                }
            } else {
                //没查询到信息不做处理
                certification.setDescription(jsonObject.getString("message"));
            }
        } catch (Exception e) {
            log.error("调用第三方实名认证接口失败 = {}", e);
            //认证执行失败不改状态由客服人工审核
            certification.setDescription("调用第三方实名认证接口失败");
        }
        return status;
    }
    /**
     * 实名认证明细
     *
     * @param userId 用户id
     * @return
     */
    @Override
    public Certification detail(long userId) {
        return this.getOne(new QueryWrapper<Certification>().eq("delete_flag", RecoverConst.DEL_FLAG_FALSE).eq("user_id", userId));
        }

    /**
     * 查看实名认证详情
     *
     * @param certificationId 实名认证id
     * @return
     */
    @Override
    public CertificationResult findById(Integer certificationId) {
        Certification certification = Optional.ofNullable(this.getById(certificationId)).orElseThrow(() -> new ServiceException(400, "查询实名认证失败"));
        CertificationResult certificationResult = new CertificationResult();
        ToolUtil.copyProperties(certification, certificationResult);
        certificationResult.setUserName(Optional.ofNullable(userService.getById(certification.getUserId())).orElse(new User()).getName());
            return certificationResult;
    }

    @Override
    public void delete(CertificationParam param) {
        this.removeById(getKey(param));
    }

    @Override
    public void update(CertificationParam param) {
        Certification oldEntity = getOldEntity(param);
        Certification newEntity = getEntity(param);
        ToolUtil.copyProperties(newEntity, oldEntity);
        this.updateById(newEntity);
    }

    @Override
    public CertificationResult findBySpec(CertificationParam param) {
        return null;
    }

    @Override
    public List<CertificationResult> findListBySpec(CertificationParam param) {
        return null;
    }

    @Override
    public LayuiPageInfo findPageBySpec(String status, String userName, String modeFlag) {
        Page pageContext = getPageContext();
        IPage page = this.baseMapper.customPageList(pageContext, status, userName, modeFlag);
        return LayuiPageFactory.createPageInfo(page);
    }

    private Serializable getKey(CertificationParam param) {
        return param.getCertificationId();
    }

    private Page getPageContext() {
        return LayuiPageFactory.defaultPage();
    }

    private Certification getOldEntity(CertificationParam param) {
        return this.getById(getKey(param));
    }

    private Certification getEntity(CertificationParam param) {
        Certification entity = new Certification();
        ToolUtil.copyProperties(param, entity);
        return entity;
    }

    /**
     * 通过用户id获取实名认证信息
     *
     * @param userId 用户id
     * @return
     */
    @Override
    public Certification findByUserId(long userId) {
        return this.getOne(new QueryWrapper<Certification>().eq("delete_flag", RecoverConst.DEL_FLAG_FALSE).eq("user_id", userId));
    }

    /**
     * 通过用户id获取实名认证信息，sys工程使用
     *
     * @param userId 用户id
     * @return
     */
    @Override
    public JSONObject findJSONByUserId(long userId) {
        Certification certification = this.getOne(new QueryWrapper<Certification>().eq("delete_flag", RecoverConst.DEL_FLAG_FALSE).eq("user_id", userId));
        if (certification != null) {
            return JSONObject.parseObject(JSONObject.toJSONString(certification));
        }
        return null;
    }

    /**
     * 审批通过
     *
     * @param certificationParam
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean approveSuccess(CertificationParam certificationParam) {
        Certification certification = new Certification();
        certification.setUpdateTime(new Date());
        certification.setCertificationId(certificationParam.getCertificationId());
        certification.setDescription(certificationParam.getDescription());
        certification.setStatus(CertificationStatus.PASS.getCode());
        certification.setUserId(certificationParam.getUserId());
        certification.setName(certificationParam.getName());
        if (this.updateById(certification)) {
            //修改实名认证状态
            User user2 = new User();
            user2.setUserId(certificationParam.getUserId());
            user2 = userService.getById(user2);
            if (user2.getMessagePush().equals("0")) {
                //发送实名认证成功通知
                sendMessageToUser(certification, user2.getOpenid());
            }
            user2.setCertificationStatus(CertificationStatus.PASS.getCode());
            user2.setRealName(certificationParam.getName());
            return userService.updateById(user2);
        }
        return false;

    }

    private void sendMessageToUser(Certification certification, String openId) {
        MessageDTO messageDTO = new MessageDTO();
        messageDTO.setUserIdList(Collections.singletonList(openId));
        messageDTO.setMessageType(MessageType.CERTIFICATION);
        WxMessageDTO wxMessageDTO = new WxMessageDTO();
        wxMessageDTO.setFirst("您提交的身份证实名认证申请已通过审核");
        wxMessageDTO.setKeyword1(certification.getName());
        wxMessageDTO.setKeyword2("认证成功");
        wxMessageDTO.setRemark("感谢您的支持，现在您可以进行后续操作了");
        messageDTO.setData(wxMessageDTO);
        messageService.sendTemplateMessage(messageDTO);
    }

    /**
     * 审批拒绝
     *
     * @param certificationParam
     * @return
     */
    @Override
    public boolean approveFail(CertificationParam certificationParam) {
        Certification certification = new Certification();
        certification.setUpdateTime(new Date());
        certification.setCertificationId(certificationParam.getCertificationId());
        certification.setDescription(certificationParam.getDescription());
        certification.setStatus(CertificationStatus.REFUSE.getCode());
        if (this.updateById(certification)) {
            //修改实名认证状态
            User user2 = new User();
            user2.setUserId(certificationParam.getUserId());
            user2.setCertificationStatus(CertificationStatus.REFUSE.getCode());
            userService.updateById(user2);
            return messageNoticeService.add("实名认证失败通知", certificationParam.getDescription(), "1", certificationParam.getUserId());
        }
        return false;
    }

    /**
     * 三方实名认证
     *
     * @param cardNo, realName
     * @return {
     *     "error_code": 0,
     *     "reason": "Success",
     *     "result": {}
     *
     *"area":"山西省太原市清徐县",
     *"sex":"男",
     *"birthday":"1985-4-10"
            *
    }
     *
     *} 0 表示成功 206501 表示 认证中心库中无此身份证记录
     * @throws Exception
     */
    private JSONObject thirdCertification(String name, String idCard) throws Exception {
        String source = "market";
        Calendar cd = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
        String datetime = sdf.format(cd.getTime());
        String auth = this.calcAuthorization(source, datetime);
        Map<String, String> headers = new HashMap<String, String>();
        headers.put("X-Source", source);
        headers.put("X-Date", datetime);
        headers.put("Authorization", auth);
        Map<String, Object> params = new HashMap<>();
        params.put("idcard", idCard);
        params.put("name", name);
        String result = HttpUtil.sendGetAndHeader(calcUrl, headers, params);
        JSONObject jsonObject = JSON.parseObject(result);
        return jsonObject;
    }

    /**
     * 签名
     *
     * @return
     */
    private String calcAuthorization(String source, String datetime) throws Exception {
        String signStr = "x-date: " + datetime + "\n" + "x-source: " + source;
        Mac mac = Mac.getInstance("HmacSHA1");
        Key sKey = new SecretKeySpec(secretKey.getBytes("UTF-8"), mac.getAlgorithm());
        mac.init(sKey);
        byte[] hash = mac.doFinal(signStr.getBytes("UTF-8"));
        String sig = new BASE64Encoder().encode(hash);

        String auth = "hmac id=\"" + secretId + "\", algorithm=\"hmac-sha1\", headers=\"x-date x-source\", signature=\"" + sig + "\"";
        System.out.println(auth);
        return auth;
    }
}
